package study.service.security;

import java.util.Collection;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.dao.DataAccessException;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.google.common.collect.Lists;

import study.domain.user.Authority;
import study.domain.user.User;
import study.service.user.UserService;

/**
 * 사용자 인증 서비스
 *
 * @author Barney Kim
 */
public class UserAuthenticationService implements UserDetailsService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private UserService userService;

	@Autowired
	private MessageSourceAccessor messageSource;

	private RoleHierarchy roleHierarchy;

	public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
		this.roleHierarchy = roleHierarchy;
	}

	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		User user = userService.getUser(username);
		if(user == null) {
			throw new UsernameNotFoundException(
				messageSource.getMessage("authentication.user.not_found", new Object[] {username}));
		}
		logger.debug("{}", user);
		Collection<GrantedAuthority> reachableGrantedAuthorities = Lists.newArrayList();
		if(user.isEnabled()) {
			List<Authority> authorities = userService.getAuthorities(user.getUserId());
			if(CollectionUtils.isNotEmpty(authorities)) {
				List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
				for(Authority a : authorities) {
					grantedAuthorities.add(new GrantedAuthorityImpl(a.getRole()));
				}
				if(roleHierarchy != null) {
					reachableGrantedAuthorities =
						roleHierarchy.getReachableGrantedAuthorities(grantedAuthorities);
				} else {
					reachableGrantedAuthorities = grantedAuthorities;
				}
			}
		}
		return new org.springframework.security.core.userdetails.User(
			user.getUsername(), // username
			user.getPassword(), // password
			user.isEnabled(), // enabled
			true, // accountNonExpired
			true, // credentialsNonExpred
			true, // accountNonLocked
			reachableGrantedAuthorities); // authorities
	}

}
